package com.mask.im.business.service.impl;


import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.BooleanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mask.im.business.common.constant.CommonConstant;
import com.mask.im.business.common.model.PageResult;
import com.mask.im.business.common.model.RestResponse;
import com.mask.im.business.model.entity.UserAuth;
import com.mask.im.business.model.entity.UserInfo;
import com.mask.im.business.enums.SliderFlagTypeEnum;
import com.mask.im.business.common.exception.BusinessException;
import com.mask.im.business.mapper.UserAuthMapper;
import com.mask.im.business.mapper.UserInfoMapper;
import com.mask.im.business.model.dto.*;
import com.mask.im.business.model.vo.*;
import com.mask.im.business.service.RedisService;
import com.mask.im.business.service.TokenService;
import com.mask.im.business.service.UserAuthService;
import com.mask.im.business.strategy.impl.context.RecoverAccountContext;
import com.mask.im.business.strategy.impl.context.RecoverPasswordContext;
import com.mask.im.business.common.util.BeanCopy;
import com.mask.im.business.common.util.IpUtil;
import com.mask.im.business.common.util.PageUtil;
import com.mask.im.business.common.util.MaskIMSecurityUtils;
import com.mask.im.business.common.constant.RedisConstant;
import com.mask.im.business.common.constant.RegisterConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.*;

import static com.mask.im.business.common.constant.CommonConstant.*;

@Slf4j
@Service
public class UserAuthServiceImpl extends ServiceImpl<UserAuthMapper, UserAuth> implements UserAuthService {

	@Autowired
	private RedisService redisService;

	@Autowired
	private HttpServletRequest request;

	@Autowired
	private PasswordEncoder passwordEncoder;

	@Autowired
	private UserDetailsServiceImpl userDetailsService;

	@Autowired
	private UserInfoMapper userInfoMapper;

	@Resource
	private TokenService tokenService;

	@Autowired
	private RecoverAccountContext recoverAccountContext;

	@Autowired
	private RecoverPasswordContext recoverPasswordContext;


	@Transactional(rollbackFor = Exception.class)
	@Override
	public RestResponse<?> phoneLogin(PhoneLoginVO phoneLoginVO) {
		//校验
		verifyPhoneLogin(phoneLoginVO);
		UserAuth userAuth = this.baseMapper.selectOne(new LambdaQueryWrapper<UserAuth>()
				.eq(UserAuth::getPhone, phoneLoginVO.getPhone()));
		String ipAddress = IpUtil.getIpAddress(request);
		String ipSource = IpUtil.getIpSource(ipAddress);

		UserInfo userInfo = userInfoMapper.selectById(userAuth.getUserInfoId());
		if (!userInfo.getDisable().equals(CommonConstant.Locked)) {
			throw new BusinessException("账号已被锁定");
		}
		//直接登入
		UserAuth auth = BeanCopy.singleCopy(userAuth, UserAuth.class);
		auth.setLastLoginTime(new Date());
		auth.setIpAddress(ipAddress);
		auth.setIpSource(ipSource);
		auth.setLoginType(RegisterConstant.PHONE_TYPE);
		this.updateById(auth);
		UserDetailsDTO userDetailsDTO = new UserDetailsDTO();
//                saveSecurityContextHolder(auth);

		String token = tokenService.createToken(userDetailsDTO);
		UserInfoDTO userLoginDTO = BeanCopy.singleCopy(userDetailsDTO, UserInfoDTO.class);
		userLoginDTO.setToken(token);
		redisService.del(RedisConstant.PHONE_LOGIN + phoneLoginVO.getPhone());
		return RestResponse.ok(userLoginDTO);
	}

	@Override
	public void logout() {
		String currentUserId = null;
		try {
			currentUserId = MaskIMSecurityUtils.getUserId().toString();
		} catch (Exception ignored) {

		}
		if (currentUserId != null) redisService.hDel(RedisConstant.USER_LOGIN, currentUserId);

	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public RestResponse<?> giteeLogin(String accessToken) {
//		SocialLoginService gitee = socialLoginFactorContext.executeLoginStrategy("giteeProvider");
//		return gitee.doGetUserLogin(accessToken);
		return null;
	}

	@Override
	public RestResponse<String> getAccessToken(String type, String code) {

//		String accessToken = accessTokenFactorContext.executeAccessToken(type).getAccessToken(code);
//		return RestResponse.ok(accessToken);
		return RestResponse.ok();
	}

	@Transactional(rollbackFor = Exception.class)
	@Override
	public RestResponse<?> gitHubLogin(String accessToken) {
//		SocialLoginService gitee = socialLoginFactorContext.executeLoginStrategy("gitHubProvider");
//		return gitee.doGetUserLogin(accessToken);
		return null;
	}

	@Override
	public RestResponse<IdentityVFDTO> getLoginAccount(String loginAccount) {
		UserAuth userAuth = this.baseMapper.selectOne(new LambdaQueryWrapper<UserAuth>()
				.eq(UserAuth::getUsername, loginAccount).or()
				.eq(UserAuth::getEmail, loginAccount).or()
				.eq(UserAuth::getPhone, loginAccount));
		if (Objects.nonNull(userAuth)) {
			IdentityVFDTO earlyIdentityVFDTO = BeanCopy.singleCopy(userAuth, IdentityVFDTO.class);
			IdentityVFDTO identityVFDTO = wrapLoginType(earlyIdentityVFDTO, loginAccount);
			return RestResponse.ok(identityVFDTO);
		}
		return RestResponse.fail("该账号不存在，请重新输入");
	}

	@Override
	public RestResponse<?> getUserAuthId(String id) {
		UserAuth userAuth = this.baseMapper.selectById(id);
		VerifyDataDTO verifyDataDTO = BeanCopy.singleCopy(userAuth, VerifyDataDTO.class);
		return RestResponse.ok(verifyDataDTO);
	}

	@Override
	public RestResponse<String> authentication(String type, IdentityVerificationVO verificationVO) {
		return recoverPasswordContext.executeRecoverPassword(type, verificationVO);
	}

	@Override
	public RestResponse<String> forgotPasswordUpdate(UpdatePasswordVO updatePasswordVO) {
		if (!updatePasswordVO.getNewPassword().equals(updatePasswordVO.getConfirmPassword())) {
			throw new BusinessException("两次密码输入不一致");
		}
		String userAuthId = (String) redisService.hGet(CommonConstant.PASSWORD_MODIFICATION, CommonConstant.PASSWORD_RECOVERY + updatePasswordVO.getToken());
		if (Objects.isNull(userAuthId)) {
			throw new BusinessException("错误请求，请刷新页面");
		}
		UserAuth userAuth = this.baseMapper.selectById(Integer.parseInt(userAuthId));
		userAuth.setPassword(passwordEncoder.encode(updatePasswordVO.getNewPassword()));
		this.baseMapper.updateById(userAuth);
		redisService.hDel(CommonConstant.PASSWORD_MODIFICATION, CommonConstant.PASSWORD_RECOVERY + updatePasswordVO.getToken());
		return RestResponse.ok("修改密码成功");
	}

	@Override
	public List<String> findAccount(String type, IdentityVerificationVO verificationVO) {
		return recoverAccountContext.executeRecoverPassword(type, verificationVO);
	}

	@Override
	public void updateEmailOrPhone(UpdateEmailOrPhoneVO updateEmailOrPhoneVO) {
		verifyUpdateEmailOrPhone(updateEmailOrPhoneVO);
		UserAuth userAuth = this.baseMapper.selectOne(new LambdaQueryWrapper<UserAuth>()
				.eq(UserAuth::getUserInfoId, updateEmailOrPhoneVO.getUserInfoId()));
		if (updateEmailOrPhoneVO.getType().equals(CommonConstant.PHONE)) {

			if (userAuth.getPhone() != null && userAuth.getPhone().equals(updateEmailOrPhoneVO.getPhone())) {
				throw new BusinessException("你已经绑定该手机号");
			}
			Integer count = this.baseMapper.selectCount(new LambdaQueryWrapper<UserAuth>()
					.eq(UserAuth::getPhone, updateEmailOrPhoneVO.getPhone()));

			if (count > 0) {
				throw new BusinessException("手机号已经被其他账号绑定");
			}
			this.baseMapper.update(null, new LambdaUpdateWrapper<UserAuth>()
					.set(UserAuth::getPhone, updateEmailOrPhoneVO.getPhone()).eq(UserAuth::getUserInfoId,
							updateEmailOrPhoneVO.getUserInfoId()));
			redisService.del(RedisConstant.PHONE_UPDATE_BIND + updateEmailOrPhoneVO.getPhone());
		} else if (updateEmailOrPhoneVO.getType().equals(CommonConstant.EMAIL)) {
			if (userAuth.getEmail() != null && userAuth.getEmail().equals(updateEmailOrPhoneVO.getPhone())) {
				throw new BusinessException("你已经绑定该邮箱号");
			}
			Integer count = this.baseMapper.selectCount(new LambdaQueryWrapper<UserAuth>()
					.eq(UserAuth::getEmail, updateEmailOrPhoneVO.getEmail()));
			if (count > 0) {
				throw new BusinessException("邮箱号已经被其他账号绑定");
			}
			this.baseMapper.update(null, new LambdaUpdateWrapper<UserAuth>()
					.set(UserAuth::getEmail, updateEmailOrPhoneVO.getEmail()).eq(UserAuth::getUserInfoId,
							updateEmailOrPhoneVO.getUserInfoId()));
			redisService.del(RedisConstant.EMAIL_UPDATE_BIND + updateEmailOrPhoneVO.getEmail());
		}
	}

	@Override
	public RestResponse<?> QQLogin(String accessToken) {
//		SocialLoginService qq = socialLoginFactorContext.executeLoginStrategy("QQProvider");
//		return qq.doGetUserLogin(accessToken);
		return null;
	}

	@Override
	public RestResponse<String> getSliderFlag(Integer flag) {
		long millis = System.currentTimeMillis();
		redisService.set(SliderFlagTypeEnum.getSliderFlagType(flag),millis);
		return RestResponse.ok(String.valueOf(millis));
	}

	@Override
	public RestResponse<SliderLocationDTO> getSliderLocation(String type) {
		Random random = new Random();
		long millis = System.currentTimeMillis();
		Integer xl = random.nextInt(100) + 100;
		Integer yl = random.nextInt(100) + 60;
		if (type.equals("accountLogin")){
			SliderLocationDTO sliderLocationDTO = new SliderLocationDTO();
			sliderLocationDTO.setXl(xl);
			sliderLocationDTO.setYl(yl);
			sliderLocationDTO.setSliderName(passwordEncoder.encode(SLIDER_ACCOUNTLOGIN_PREFIX)+"_"+millis);
			if (Objects.nonNull(redisService.get(SLIDER_ACCOUNTLOGIN_PREFIX+millis))){
				this.getSliderLocation(type);
			}
			redisService.set(SLIDER_ACCOUNTLOGIN_PREFIX+millis,xl+"_"+yl,60);
			return RestResponse.ok(sliderLocationDTO);
		}else if (type.equals("SmsLogin")){
			SliderLocationDTO sliderLocationDTO = new SliderLocationDTO();
			sliderLocationDTO.setXl(xl);
			sliderLocationDTO.setYl(yl);
			sliderLocationDTO.setSliderName(passwordEncoder.encode(SLIDER_SMSLOGIN_PREFIX)+"_"+millis);
			if (Objects.nonNull(redisService.get(SLIDER_SMSLOGIN_PREFIX+millis))){
				this.getSliderLocation(type);
			}
			redisService.set(SLIDER_SMSLOGIN_PREFIX+millis,xl+"_"+yl,60);
			return RestResponse.ok(sliderLocationDTO);
		}
		return RestResponse.fail();
	}

	@Override
	public RestResponse<SliderLocationDTO> verifySliderLocation(VerifySliderLocationVO sliderLocationDTO) {
		UserAuth userAuth = this.baseMapper.selectOne(new LambdaQueryWrapper<UserAuth>()
				.eq(UserAuth::getUsername, sliderLocationDTO.getAccount()).or()
				.eq(UserAuth::getEmail,sliderLocationDTO.getAccount()).or()
				.eq(UserAuth::getPhone,sliderLocationDTO.getAccount()));
		if (Objects.isNull(userAuth)) {
			return RestResponse.fail("用户不存在");
		}
		if (sliderLocationDTO.getType().equals("accountLogin")){
			String sliderName = sliderLocationDTO.getSliderName();
			String ciphertext = sliderName.substring(0,sliderName.indexOf("_"));
			if (!passwordEncoder.matches(SLIDER_ACCOUNTLOGIN_PREFIX,ciphertext)){
				throw new BusinessException("网络异常，请重新操作！");
			}
			String timestamp = sliderName.substring(sliderName.indexOf("_")+1);
			String location = String.valueOf(redisService.get(SLIDER_ACCOUNTLOGIN_PREFIX + timestamp));
			int xl = Integer.parseInt(location.substring(0, location.indexOf("_")));
			if (sliderLocationDTO.getCurrentLocation() >= xl - 5 && sliderLocationDTO.getCurrentLocation() <= xl + 5){
				redisService.set(SLIDER_PASS_ACCOUNTLOGIN+sliderLocationDTO.getAccount(),1,30);
				return RestResponse.ok("校验成功！");
			}else {
				redisService.set(SLIDER_PASS_ACCOUNTLOGIN+sliderLocationDTO.getAccount(),0,30);
				return RestResponse.fail("校验失败，请重新校验！");
			}
		}else if (sliderLocationDTO.getType().equals("SmsLogin")){
			String sliderName = sliderLocationDTO.getSliderName();
			String ciphertext = sliderName.substring(0,sliderName.indexOf("_"));
			if (!passwordEncoder.matches(SLIDER_SMSLOGIN_PREFIX,ciphertext)){
				throw new BusinessException("网络异常，请重新操作！");
			}
			String timestamp = sliderName.substring(sliderName.indexOf("_")+1);
			String location = String.valueOf(redisService.get(SLIDER_SMSLOGIN_PREFIX + timestamp));
			int xl = Integer.parseInt(location.substring(0, location.indexOf("_")));
			if (sliderLocationDTO.getCurrentLocation() >= xl - 5 && sliderLocationDTO.getCurrentLocation() <= xl + 5){
				redisService.set(SLIDER_PASS_SMSLOGIN+sliderLocationDTO.getAccount(),1,30);
				return RestResponse.ok("校验成功！");
			}else {
				redisService.set(SLIDER_PASS_SMSLOGIN+sliderLocationDTO.getAccount(),0,30);
				return RestResponse.fail("校验失败，请重新校验！");
			}
		}
		return RestResponse.fail("出错啦！请刷新页面");
	}

	@Override
	public PageResult<UserAdminDTO> listUsers(ConditionVO conditionVO) {
		Integer count = this.baseMapper.countUser(conditionVO);
		if (count == 0) {
			return PageResult.<UserAdminDTO>builder().build();
		}
		List<UserAdminDTO> UserAdminDTOs = this.baseMapper.listUsers(PageUtil.getCurrentPage(), PageUtil.getSize(), conditionVO);
		return PageResult.<UserAdminDTO>builder().data(UserAdminDTOs).total(count).build();
	}

	@Override
	public void updateAdminPassword(PasswordVO passwordVO) {
		UserAuth user = this.baseMapper.selectOne(new LambdaQueryWrapper<UserAuth>()
				.eq(UserAuth::getUserInfoId, MaskIMSecurityUtils.getUserId()));
		if (Objects.nonNull(user) && passwordEncoder.matches(passwordVO.getOldPassword(), user.getPassword())) {
			UserAuth userAuth = UserAuth.builder()
					.id(user.getId())
					.password(passwordEncoder.encode(passwordVO.getNewPassword()))
					.build();
			this.baseMapper.updateById(userAuth);
		} else {
			throw new BusinessException("旧密码不正确");
		}
	}

	private void verifyUpdateEmailOrPhone(UpdateEmailOrPhoneVO updateEmailOrPhoneVO) {
		if (updateEmailOrPhoneVO.getType().equals(CommonConstant.EMAIL)) {
			if (!Validator.isEmail(updateEmailOrPhoneVO.getEmail())) {
				throw new BusinessException("邮箱格式不正确");
			}
			Boolean aBoolean = redisService.hasKey(RedisConstant.EMAIL_UPDATE_BIND + updateEmailOrPhoneVO.getEmail());
			if (BooleanUtil.isFalse(aBoolean)) {
				throw new BusinessException("验证码已过期,请重新获取");
			}
		}
		if (updateEmailOrPhoneVO.getType().equals(CommonConstant.PHONE)) {
			if (!Validator.isMobile(updateEmailOrPhoneVO.getPhone())) {
				throw new BusinessException("手机格式不正确");
			}
			Boolean aBoolean = redisService.hasKey(RedisConstant.PHONE_UPDATE_BIND + updateEmailOrPhoneVO.getPhone());
			if (BooleanUtil.isFalse(aBoolean)) {
				throw new BusinessException("验证码已过期,请重新获取");
			}
		}
	}

	public IdentityVFDTO wrapLoginType(IdentityVFDTO identityVFDTO, String loginAccount) {

		if (Validator.isEmail(loginAccount)) {
			identityVFDTO.setLoginType(RegisterConstant.EMAIL_TYPE);
		} else if (Validator.isMobile(loginAccount)) {
			identityVFDTO.setLoginType(RegisterConstant.PHONE_TYPE);
		} else {
			identityVFDTO.setLoginType(RegisterConstant.USERNAME_TYPE);
		}
		return identityVFDTO;
	}

	/**
	 * 获取UserDetailsDTO
	 *
	 * @param auth
	 * @return
	 */
	public UserDetailsDTO getUserDetail(UserAuth auth) {
		return userDetailsService.convertUserDetail(auth, request);
	}


	/**
	 * 校验
	 *
	 * @param phoneLoginVO
	 */
	public void verifyPhoneLogin(PhoneLoginVO phoneLoginVO) {

		if (!Validator.isMobile(phoneLoginVO.getPhone())) {
			throw new BusinessException("手机格式错误");
		}
		Integer count = this.baseMapper.selectCount(new LambdaQueryWrapper<UserAuth>()
				.eq(UserAuth::getPhone, phoneLoginVO.getPhone()));
		if (count <= 0) {
			throw new BusinessException("手机号尚未注册");
		}
		String code = (String) redisService.get(RedisConstant.PHONE_LOGIN + phoneLoginVO.getPhone());
		if (Objects.isNull(code)) {
			throw new BusinessException("验证码已过期");
		}

		if (phoneLoginVO.getCode() == null || !code.equals(phoneLoginVO.getCode())) {
			throw new BusinessException("验证码不正确");
		}
	}
}
